﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using WarningForeverClone.src.weapons;
using WarningForeverClone.src.util;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace WarningForeverClone.src.ships
{
    public class Boss : ShipPart
    {
        const string texturePath = "Textures/Boss/hull";
        const int maxHealth = 250;
        float speed = 0.6f;

        static int numPartsPerSide = 5; //num weapons per side of boss
        const int spacing = 60; //spacing in pixels between the weapons

        const char GREEN_LASER = 'g';
        const char BLUE_LASER = 'b';
        const char RED_LASER = 'r';
        const char ROCKET = 'k';

        //Limits how long before the next shot can be fired
        int[] maxLaserRechargeTime;
        int[] laserRechargeTime;
        List<Weapon> shotsFired;

        Vector2 position;
        int direction = 1;
        float moveFlipPoint;
        static int maxMoveLength = 601;
        string weapons;

        Random random = new Random();
        Ship player;

        int level;

        public static Texture2D greenHullShield, blueHullShield, redHullShield;

        private Boss(Ship player)
            : base(-1, null, texturePath, Vector2.Zero, true, maxHealth)
        {
            position = Vector2.Zero;
            shotsFired = new List<Weapon>();
            this.player = player;
        }

        public override void LoadContent(ContentManager contentManager)
        {
            base.LoadContent(contentManager);
            maxMoveLength = MainGame.ScreenSize.X - Texture.Width - 20;
            moveFlipPoint = random.Next(maxMoveLength);

            greenHullShield = contentManager.Load<Texture2D>("Textures/Boss/greenHullShield");
            blueHullShield = contentManager.Load<Texture2D>("Textures/Boss/blueHullShield");
            redHullShield = contentManager.Load<Texture2D>("Textures/Boss/redHullShield");
        }

        public void Move()
        {
            position.X += speed * direction;
            if ((direction == 1 && position.X >= moveFlipPoint)
                || (direction == -1 && position.X <= moveFlipPoint))
            {
                direction *= -1;
                if (direction == 1)
                {
                    moveFlipPoint = random.Next(maxMoveLength) + position.X;
                    if (moveFlipPoint > 1280 - Width)
                        moveFlipPoint = 1280 - Width;
                }
                else
                {
                    moveFlipPoint = random.Next(maxMoveLength) - position.X;
                    if (moveFlipPoint < 0)
                        moveFlipPoint = 1;
                }
            }
        }
        public void Update(Ship player, PlayerModel model)
        {
            UpdateShots(player, model);

            int index = 0;
            foreach (char weapon in weapons)
            {
                laserRechargeTime[index]++;

                if (laserRechargeTime[index] >= maxLaserRechargeTime[index])
                {
                    if (random.Next(1000) < 10 + level)
                    {
                        Fire(parts[index], weapon);
                        laserRechargeTime[index] = 0;
                    }
                }
                index++;

            }
        }
        private void UpdateShots(Ship player, PlayerModel model)
        {

            Matrix playerTrans = Matrix.CreateTranslation(new Vector3(player.Position, 0));

            //check collision with player
            for (int x = 0; x < shotsFired.Count; x++)
            {
                Weapon w = (Weapon)shotsFired[x];


                Matrix weaponTrans = Matrix.CreateTranslation(new Vector3(-new Vector2(w.Texture.Width / 2, w.Texture.Height / 2), 0.0f)) *
                        Matrix.CreateRotationZ((float)w.Rotation) *
                        Matrix.CreateTranslation(new Vector3(w.Position, 0.0f));

                if (w is GreenLaser)
                {
                    if (NonRotatedPerPixelCollision(w, player.Position, new Vector2(player.Width, player.Height), Ship.bits))
                    {
                        w.Alive = false;
                        MainGame.particleManager.CreateHit(player.Position + new Vector2(player.Width / 2, player.Height / 2));
                        player.health -= w.Damage;
                        model.logPlayerHit(w);
                    }
                }
                else
                {
                    if (RotatedPerPixelCollision(weaponTrans, w.Texture.Width, w.Texture.Height, w.TextureData,
                                        playerTrans, player.Width, player.Height, Ship.bits))
                    {
                        w.Alive = false;
                        MainGame.particleManager.CreateHit(player.Position + new Vector2(player.Width / 2, player.Height / 2));
                        player.health -= w.Damage;
                        model.logPlayerHit(w);
                    }
                }

                //remove weapons offscreen and ones that aren't alive
                if (!w.Alive || !w.IsOnScreen())
                {
                    DeleteShot(w);
                    x--;
                    continue;
                }

                w.Update();
            }
        }
        private void DeleteShot(Weapon w)
        {
            if (w is GreenLaser)
                greenLaserPool.Free((GreenLaser)w);
            else if (w is BlueLaser)
                blueLaserPool.Free((BlueLaser)w);
            else if (w is RedLaser)
                redLaserPool.Free((RedLaser)w);

            shotsFired.Remove(w);
        }
        private void Fire(ShipPart part, char type)
        {
            if (!part.Alive)
                return;

            if (type == GREEN_LASER)
            {
                FireGreenLaser(position + part.Offset + new Vector2((part.Width / 2) - (GreenLaser.Width / 2), part.Height));
            }
            else if (type == BLUE_LASER)
            {
                FireBlueLaser(position + part.Offset + new Vector2((part.Width / 2) - (GreenLaser.Width / 2), part.Height));
            }
            else if (type == RED_LASER)
            {
                FireRedLaser(position + part.Offset + new Vector2((part.Width / 2) - (GreenLaser.Width / 2), part.Height));
            }
            else if (type == ROCKET) //rocket
            {
                FireRocket(position + part.Offset);
            }
        }
        private void FireGreenLaser(Vector2 laucnPoint)
        {
            int speed = 5;

            GreenLaser l = greenLaserPool.Get();
            l.Reset();
            l.Position = laucnPoint + new Vector2(-10, 0);
            l.Velocity = new Vector2(0, speed);
            shotsFired.Add(l);

            GreenLaser l2 = greenLaserPool.Get();
            l2.Reset();
            l2.Position = laucnPoint + new Vector2(10, 0);
            l2.Velocity = new Vector2(0, speed);
            shotsFired.Add(l2);
        }
        private void FireBlueLaser(Vector2 laucnPoint)
        {
            const double rotation = Math.PI / 4;
            const int speed = 5;

            BlueLaser l = blueLaserPool.Get();
            l.Reset();
            l.Position = laucnPoint + new Vector2(10, 0);
            l.Velocity = new Vector2((float)(speed * Math.Cos(rotation)), (float)(speed * Math.Sin(rotation)));
            l.Rotation = -Math.PI / 2 + rotation;
            shotsFired.Add(l);

            BlueLaser l2 = blueLaserPool.Get();
            l2.Reset();
            l2.Position = laucnPoint + new Vector2(10, 0);
            l2.Velocity = new Vector2((float)(-speed * Math.Cos(rotation)), (float)(speed * Math.Sin(rotation)));
            l2.Rotation = -Math.PI / 2 - rotation;
            shotsFired.Add(l2);
        }
        private void FireRedLaser(Vector2 laucnPoint)
        {
            double rotation = Math.Atan2((player.Position.Y + player.Height / 2) - laucnPoint.Y, (player.Position.X + player.Width / 2) - laucnPoint.X);
            int speed = 10;

            RedLaser l = redLaserPool.Get();
            l.Reset();
            l.Position = laucnPoint + new Vector2(10, 0);
            l.Velocity = new Vector2((float)(speed * Math.Cos(rotation)), (float)(speed * Math.Sin(rotation)));
            l.Rotation = -Math.PI / 2 + rotation;
            shotsFired.Add(l);
        }
        private void FireRocket(Vector2 launchPoint)
        {
            int speed = Rocket.speed;

            Rocket r = rocketPool.Get();
            r.Reset();
            r.Target = player;
            r.Position = launchPoint + new Vector2(10, 0);
            r.Velocity = new Vector2(0, speed);
            r.Rotation = Math.Atan2(r.Velocity.Y, r.Velocity.X) + Math.PI / 2;
            shotsFired.Add(r);
        }

        protected override void CheckMyCollisions(List<Weapon> weapons)
        {
            for (int x = 0; x < weapons.Count; x++)
            {
                Weapon w = weapons[x];
                if (RectRectCollision(position + new Vector2(364, 180), new Vector2(80, 41),
                                        w.Position, new Vector2(w.Texture.Width, w.Texture.Height)))
                {
                    if (w is GreenLaser && ShieldType == 0 ||
                        w is BlueLaser && ShieldType == 1 ||
                        w is RedLaser && ShieldType == 2)
                    {
                        w.Collide(this);
                        MainGame.particleManager.CreateMiss(w.Position);
                    }
                    else
                    {

                        w.Collide(this);
                        MainGame.particleManager.CreateHit(w.Position);
                        Health -= w.Damage;
                    }
                }
                if (NonRotatedPerPixelCollision(w, position, new Vector2(Width, Height), textureBits[texturePath]))
                {
                    MainGame.particleManager.CreateMiss(w.Position);
                    w.Alive = false;
                }
            }
        }

        public void KeepOnScreen(int left, int top, int right, int bottom)
        {
            if (position.X < left)
                position.X = left;
            if (position.X + Width > right)
                position.X = right - Width;

            if (position.Y < top)
                position.Y = top;
            if (position.Y + Height > bottom)
                position.Y = bottom - Height;
        }

        public Vector2 Position
        {
            get { return position; }
        }
        public List<Weapon> ShotsFired
        {
            get { return shotsFired; }
        }
        public float Speed
        {
            get { return speed; }
        }

        /// <summary>
        /// Construct a ShipPart that corresponds to the given weapon type
        /// </summary>
        /// <param name="boss">Boss that will have this weapon</param>
        /// <param name="weaponType">The type of weapon </param>
        /// <param name="offset">Offset on boss hull to place weapon</param>
        /// <param name="inFront"></param>
        /// <returns></returns>
        private static ShipPart ConstructPartFromType(Boss boss, char weaponType, Vector2 offset, bool inFront, int shieldType)
        {
            const int laserHealth = 40;
            const int rocketHealth = 100;

            const string blueLaserTexturePath = "Textures/Boss/blueLauncher";
            const string redLaserTexturePath = "Textures/Boss/redLauncher";
            const string greenLaserTexturePath = "Textures/Boss/greenLauncher";
            const string rocketTexturePath = "Textures/Boss/flamethrower";

            ShipPart part = null;
            if (weaponType == GREEN_LASER)
            {
                part = new ShipPart(0, boss, greenLaserTexturePath, offset, inFront, laserHealth);
            }
            else if (weaponType == BLUE_LASER)
            {
                part = new ShipPart(1, boss, blueLaserTexturePath, offset, inFront, rocketHealth);
            }
            else if (weaponType == RED_LASER)
            {
                part = new ShipPart(2, boss, redLaserTexturePath, offset, inFront, rocketHealth);
            }
            else if (weaponType == ROCKET)
            {
                part = new ShipPart(4, boss, rocketTexturePath, offset, inFront, rocketHealth);
            }
            part.ShieldType = shieldType;

            return part;
        }
        /// <summary>
        /// Creates a boss with the specified weapons
        /// </summary>
        /// <param name="weaponConfiguration">The string specifing which weapons to add</param>
        /// <returns></returns>
        public static Boss CreateBoss(int level, Ship player, int hullShieldType, float speed = 0.6f, String weaponConfiguration = "g1b1b0k0g2b2r1b1r0k0")
        {

            Boss boss = new Boss(player);
            boss.level = level;
            boss.ShieldType = hullShieldType;
            boss.speed = speed;

            numPartsPerSide = Math.Min(5, level);

            boss.maxLaserRechargeTime = new int[numPartsPerSide * 2];

            //trim weapon config
            weaponConfiguration = weaponConfiguration.Substring(0, numPartsPerSide * 4);

            //construct left half of boss
            string weaponConfigLeft = weaponConfiguration.Substring(0, weaponConfiguration.Length / 2);
            for (int x = 0; x < numPartsPerSide * 2; x += 2)
            {
                char weaponType = weaponConfigLeft[x];
                boss.parts.Add(ConstructPartFromType(boss, weaponType, new Vector2(87 + spacing * (x / 2), 178),
                    false, Int32.Parse(weaponConfigLeft[x + 1].ToString())));
                if (weaponType == GREEN_LASER)
                {
                    boss.maxLaserRechargeTime[x / 2] = 60;
                }
                else if (weaponType == BLUE_LASER)
                {
                    boss.maxLaserRechargeTime[x / 2] = 80;
                }
                else if (weaponType == RED_LASER)
                {
                    boss.maxLaserRechargeTime[x / 2] = 120;
                }
                else if (weaponType == ROCKET)
                {
                    boss.maxLaserRechargeTime[x / 2] = 200;
                }
            }

            //construct right half of boss
            string weaponConfigRight = weaponConfiguration.Substring(weaponConfiguration.Length / 2);
            for (int x = 0; x < numPartsPerSide * 2; x += 2)
            {
                char weaponType = weaponConfigRight[x];
                boss.parts.Add(ConstructPartFromType(boss, weaponType, new Vector2(655 - spacing * (x / 2), 178),
                    false, Int32.Parse(weaponConfigRight[x + 1].ToString())));
                if (weaponType == GREEN_LASER)
                {
                    boss.maxLaserRechargeTime[numPartsPerSide + x / 2] = 60;
                }
                else if (weaponType == BLUE_LASER)
                {
                    boss.maxLaserRechargeTime[numPartsPerSide + x / 2] = 80;
                }
                else if (weaponType == RED_LASER)
                {
                    boss.maxLaserRechargeTime[numPartsPerSide + x / 2] = 120;
                }
                else if (weaponType == ROCKET)
                {
                    boss.maxLaserRechargeTime[numPartsPerSide + x / 2] = 200;
                }
            }


            String weapons = "";
            foreach (char c in weaponConfiguration)
            {
                if (Char.IsLetter(c))
                    weapons += c;
            }
            boss.weapons = weapons;
            boss.laserRechargeTime = new int[boss.weapons.Length];

            return boss;
        }

        //weapon pools to remove overhead of creating and destroying objects so quickly
        private static Pool<GreenLaser> greenLaserPool;
        private static Pool<BlueLaser> blueLaserPool;
        private static Pool<RedLaser> redLaserPool;
        private static Pool<Rocket> rocketPool;
        static Boss()
        {
            greenLaserPool = new Pool<GreenLaser>(40);
            blueLaserPool = new Pool<BlueLaser>(40);
            redLaserPool = new Pool<RedLaser>(40);
            rocketPool = new Pool<Rocket>(20);
        }
    }
}
